 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.update.standalone;

 import java.io.File ;
 import java.net.MalformedURLException ;
 import java.net.URL ;
 import java.util.ArrayList ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.SubProgressMonitor;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.update.configuration.IConfiguredSite;
 import org.eclipse.update.core.IFeature;
 import org.eclipse.update.core.ISite;
 import org.eclipse.update.core.SiteManager;
 import org.eclipse.update.core.Utilities;
 import org.eclipse.update.core.VersionedIdentifier;
 import org.eclipse.update.internal.configurator.UpdateURLDecoder;
 import org.eclipse.update.internal.core.Messages;
 import org.eclipse.update.internal.core.UpdateCore;
 import org.eclipse.update.internal.core.UpdateManagerUtils;
 import org.eclipse.update.internal.operations.DuplicateConflictsValidator;
 import org.eclipse.update.internal.operations.UpdateUtils;
 import org.eclipse.update.internal.search.SiteSearchCategory;
 import org.eclipse.update.operations.IBatchOperation;
 import org.eclipse.update.operations.IInstallFeatureOperation;
 import org.eclipse.update.operations.OperationsManager;
 import org.eclipse.update.search.BackLevelFilter;
 import org.eclipse.update.search.EnvironmentFilter;
 import org.eclipse.update.search.IUpdateSearchResultCollector;
 import org.eclipse.update.search.UpdateSearchRequest;
 import org.eclipse.update.search.UpdateSearchScope;
 import org.eclipse.update.search.VersionedIdentifiersFilter;

 /**
  * Command to install a feature.
  * <p>
  * <b>Note:</b> This class/interface is part of an interim API that is still under development and expected to
  * change significantly before reaching stability. It is being made available at this early stage to solicit feedback
  * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
  * (repeatedly) as the API evolves.
  * </p>
  * @since 3.0
  */
 public class InstallCommand extends ScriptedCommand {

     private IConfiguredSite targetSite;
     private UpdateSearchRequest searchRequest;
     private UpdateSearchResultCollector collector;
     private URL remoteSiteURL;
     private String featureId;
     private String version;

     public InstallCommand(
         String featureId,
         String version,
         String fromSite,
         String toSite,
         String verifyOnly)
         throws Exception {

         super(verifyOnly);

         try {
             this.featureId = featureId;
             this.version = version;

             //PAL foundation
 this.remoteSiteURL = new URL (UpdateURLDecoder.decode(fromSite, "UTF-8")); //$NON-NLS-1$

             // Get site to install to
 targetSite = getTargetSite(toSite);
             // if no site, try selecting the site that already has the old feature
 if (targetSite == null)
                 targetSite = UpdateUtils.getSiteWithFeature(getConfiguration(), featureId);
             // if still no site, pick the product site, if writeable
 if (targetSite == null) {
                 IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
                 for (int i = 0; i < sites.length; i++) {
                     if (sites[i].isProductSite() && sites[i].isUpdatable()) {
                         targetSite = sites[i];
                         break;
                     }
                 }
             }
             // if all else fails, pick the first updateable site
 if (targetSite == null) {
                 IConfiguredSite[] sites = getConfiguration().getConfiguredSites();
                 for (int i = 0; i < sites.length; i++) {
                     if (sites[i].isUpdatable()) {
                         targetSite = sites[i];
                         break;
                     }
                 }
             }
             // are we still checking for sites? forget about it
 if (targetSite == null)
                 throw Utilities.newCoreException(
                         Messages.Standalone_cannotInstall + featureId + " " + version, //$NON-NLS-1$
 null);
             
             UpdateSearchScope searchScope = new UpdateSearchScope();
             searchScope.addSearchSite(
                 NLS.bind( Messages.InstallCommand_site, remoteSiteURL.toExternalForm()),
                 remoteSiteURL,
                 new String [0]);

             searchRequest =
                 new UpdateSearchRequest(new SiteSearchCategory(), searchScope);
             VersionedIdentifier vid =
                 new VersionedIdentifier(featureId, version);
             searchRequest.addFilter(
                 new VersionedIdentifiersFilter(
                     new VersionedIdentifier[] { vid }));
             searchRequest.addFilter(new EnvironmentFilter());
             searchRequest.addFilter(new BackLevelFilter());

             collector = new UpdateSearchResultCollector();

         } catch (MalformedURLException e) {
             throw e;
         } catch (CoreException e) {
             throw e;
         }
     }

     /**
      */
     public boolean run(IProgressMonitor monitor) {
         try {
             monitor.beginTask(Messages.Standalone_installing, 4);
             searchRequest.performSearch(collector, new SubProgressMonitor(monitor,1));
             IInstallFeatureOperation[] operations = collector.getOperations();
             if (operations == null || operations.length == 0) {
                 throw Utilities.newCoreException(
                     Messages.Standalone_feature
                         + featureId
                         + " " //$NON-NLS-1$
 + version
                         + Messages.Standalone_notFound
                         + remoteSiteURL
                         + Messages.Standalone_newerInstalled,
                     null);
             }

             // Check for duplication conflicts
 ArrayList conflicts =
                 DuplicateConflictsValidator.computeDuplicateConflicts(
                     operations,
                     getConfiguration());
             if (conflicts != null) {
                 throw Utilities.newCoreException(Messages.Standalone_duplicate, null);
             }

             if (isVerifyOnly()) {
                 if (operations == null || operations.length == 0)
                     return false;
                 IStatus status = OperationsManager.getValidator().validatePendingChanges(operations);
                 if (status != null && status.getCode() == IStatus.ERROR)
                     throw new CoreException(status);
                 else
                     return true;
             }

             IBatchOperation installOperation =
                 OperationsManager
                     .getOperationFactory()
                     .createBatchInstallOperation(
                     operations);
             try {
                 installOperation.execute(new SubProgressMonitor(monitor,3), this);
                 System.out.println(
                     Messages.Standalone_feature
                         + featureId
                         + " " //$NON-NLS-1$
 + version
                         + Messages.Standalone_installed);
                 return true;
             } catch (Exception e) {
                 throw Utilities.newCoreException(
                     Messages.Standalone_cannotInstall + featureId + " " + version, //$NON-NLS-1$
 e);
             }
         } catch (CoreException ce) {
             StandaloneUpdateApplication.exceptionLogged();
             UpdateCore.log(ce);
             return false;
         } catch (OperationCanceledException ce) {
             return true;
         } finally {
             monitor.done();
         }
     }

     private IConfiguredSite getTargetSite(String toSite) throws Exception {
         if (toSite == null)
             return null;
             
         IConfiguredSite[] configuredSites = getConfiguration().getConfiguredSites();
         File sitePath = new File (toSite);
         File secondaryPath = sitePath.getName().equals("eclipse") ? //$NON-NLS-1$
 null : new File (sitePath, "eclipse"); //$NON-NLS-1$

         for (int i = 0; i < configuredSites.length; i++) {
             IConfiguredSite csite = configuredSites[i];
             if (UpdateManagerUtils.sameURL(csite.getSite().getURL(),sitePath.toURL()))
                 return csite;
             else if (secondaryPath != null && UpdateManagerUtils.sameURL(csite.getSite().getURL(),secondaryPath.toURL()))
                 return csite;
         }

         // extension site not found, need to create one
 if (!sitePath.exists())
             sitePath.mkdirs();
         URL toSiteURL = sitePath.toURL();
         ISite site = SiteManager.getSite(toSiteURL, null);
         if (site == null) {
             throw new Exception (Messages.Standalone_noSite + toSite);
         }
         IConfiguredSite csite = site.getCurrentConfiguredSite();
         if (csite == null) {
             csite = getConfiguration().createConfiguredSite(sitePath);
             IStatus status = csite.verifyUpdatableStatus();
             if (status.isOK())
                 getConfiguration().addConfiguredSite(csite);
             else
                 throw new CoreException(status);

             return csite;
         }
         return csite;
     }
     class UpdateSearchResultCollector implements IUpdateSearchResultCollector {
         private ArrayList operations = new ArrayList ();

         public void accept(IFeature feature) {
             if (feature
                 .getVersionedIdentifier()
                 .getIdentifier()
                 .equals(featureId)
                 && feature
                     .getVersionedIdentifier()
                     .getVersion()
                     .toString()
                     .equals(
                     version)) {
                 operations.add(
                     OperationsManager
                         .getOperationFactory()
                         .createInstallOperation(
                         targetSite,
                         feature,
                         null,
                         null,
                         null));
             }
         }
         public IInstallFeatureOperation[] getOperations() {
             IInstallFeatureOperation[] opsArray =
                 new IInstallFeatureOperation[operations.size()];
             operations.toArray(opsArray);
             return opsArray;
         }
     }
 }

